#include <stdlib.h>
#include "PS_mysql_pool.h"
#define mylogger printf

CMysqlPool::CMysqlPool(int num):m_host(NULL), m_user(NULL), m_pass(NULL),
	 m_dbname(NULL) , m_socket(NULL) ,m_port(0),m_flags(0), 
	  m_n(num)
{
}

CMysqlPool::~CMysqlPool()
{
    mf_closeConnPool();
}

int CMysqlPool::PS_mysql_init(const char *host, const char *user, 
				const char *pass, const char *dbname, 
				const char *socket, int port, unsigned int flags)
{
    m_host = host;
    m_user = user;
    m_pass = pass;
    m_dbname = dbname;
    m_socket = socket;
    port == 0? m_port = 3306: m_port = port;
    m_flags = flags;
    
    mf_openConnPool();
    
    return 0;
}

int CMysqlPool::PS_mysql_create(MYSQL **mysql)
{
    // create and set db connection
    if (*mysql == NULL)
    {
        *mysql = mysql_init(NULL);
	    // if db connection cannot be created, log err msg and cleanup.
	    if (*mysql == NULL)
	    {
		    mylogger("mysql_init %u (%s)", mysql_errno(*mysql), 
			      mysql_error(*mysql));
		    return 1;
	    }
    }

	unsigned int timeout = 3;
	if (mysql_options(*mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout))
	{
		mylogger("mysql_options Error %u (%s)", mysql_errno(*mysql),
			mysql_error(*mysql));
		return 2;
	}

#ifdef MYDEBUG
	printf("file=%s,line=%d,host=%s,user=%s,passwd=%s,dbname=%s,port=%d\n",
		__FILE__,__LINE__,m_host,m_user,m_pass,m_dbname,m_port);
#endif
    if (!mysql_real_connect(*mysql, m_host, m_user, m_pass, 
		m_dbname, m_port, m_socket, m_flags))
    {
        mylogger("mysql_real_connect %u (%s)", mysql_errno(*mysql), 
			mysql_error(*mysql));
        return 3;
    }

	uint32_t reconnect = 0x01010101;
	if(mysql_options(*mysql, MYSQL_OPT_RECONNECT, &reconnect))
	{
		mylogger("mysql_options Error %u (%s)", mysql_errno(*mysql),
			mysql_error(*mysql));
		return 4;
	}


	if (mysql_set_character_set(*mysql, "utf8mb4") != 0)
	{
		mylogger("mysql_set_character_set Error %u (%s)", mysql_errno(*mysql),
			mysql_error(*mysql));
		return 5;
	}

    return 0;
}

void CMysqlPool::PS_mysql_destroy(MYSQL *mysql)
{
    /* release DB connection */
    if (mysql != NULL)
    {
        mysql_close(mysql);
    }
}

int CMysqlPool::PS_mysql_maybe_reconnect(MYSQL **mysql)
{
 //   if (NULL == *mysql) {
 //       return -1;
 //   }
	//if (0 == mysql_ping(*mysql))
	//{
	//	return 0;
	//}

	if (NULL != *mysql) {
		if (0 == mysql_ping(*mysql))
		{
			return 0;
		}
    }
    else
    {
        m_n++;
    }

#ifdef MYDEBUG
	printf("file=%s,line=%d,info:mysql_ping failed\n",
		__FILE__,__LINE__);
#endif

    return PS_mysql_create(mysql);
}

void CMysqlPool::mf_openConnPool()
{
	for(int i=0; i<m_n; i++)
	{
		MYSQL *mysql = NULL;
		if (0 == PS_mysql_create(&mysql))
		{
			put(mysql);
		}
		else
		{
			printf("file=%s,line=%d,info:connect db error\n",
					__FILE__,__LINE__);
			exit(1);
		}
	}

//    logDaily(g_log_file_prefix, "openConnPool: create pool is ok");
}

void CMysqlPool::mf_closeConnPool()
{
#ifdef WIN32
    for(int i=0; i<m_n; i++)
    {
        MYSQL *mysql;
        mysql = get();
        PS_mysql_destroy(mysql);
    }
#else
	while( m_sem.getvalue() > 0)
    {
        MYSQL *mysql;
        mysql = get();
        PS_mysql_destroy(mysql);
    }
#endif

    //logDaily(g_log_file_prefix, "close ConnPool: close pool is ok");
}

MYSQL* CMysqlPool::get()
{
	MYSQL *mysql = NULL;

//	lk.lock();
    m_Lock.Lock();
    if (m_conn.size() > 0){
		mysql = m_conn.front();
		m_conn.pop_front();
    }
    m_Lock.UnLock();
//	lk.unlock();
	
	return mysql;
}

void CMysqlPool::put(MYSQL *mysql)
{
//	lk.lock();
    m_Lock.Lock();
	m_conn.push_back(mysql);
    m_Lock.UnLock();
//	lk.unlock();
}

MYSQL_RES* CMysqlPool::SelectRecord(const char *szSql)
{
    MYSQL *pMySql = get();
    if (pMySql == NULL && 0 != PS_mysql_maybe_reconnect(&pMySql))
    {
        put(pMySql);
	    //write log
	    mylogger("warning:mysqlerror:%s\n",mysql_error(pMySql));
        return NULL;
    }
    for (int i=0; i<2; ++i)
    {
        if (0 == mysql_query(pMySql, szSql))
        {
            break;
        }
        else if (0 == PS_mysql_maybe_reconnect(&pMySql))
        {
            continue;
        }
        else
        {
            put(pMySql);
	        mylogger("warning:mysqlerror:%s\n",mysql_error(pMySql));
            return NULL;
        }
    }
    
    MYSQL_RES *myquery = NULL;
    myquery = mysql_store_result(pMySql);
    put(pMySql);

    return myquery;
}

bool CMysqlPool::InsertRecord(const char *szSql)
{
    bool bRet = false;
    MYSQL *pMySql = get();
    if (pMySql == NULL && 0 != PS_mysql_maybe_reconnect(&pMySql))
    {
        put(pMySql);
	    mylogger("warning:mysqlerror:%s\n",mysql_error(pMySql));
        return bRet;
    }
    
    for (int i=0; i<2; ++i)
    {
        if (0 == mysql_query(pMySql, szSql))
        {
            bRet = true;
            break;
        }
        else if (0 == PS_mysql_maybe_reconnect(&pMySql))
        {
            continue;
        }
        else
        {
            put(pMySql);
	        mylogger("warning:mysqlerror:%s\n",mysql_error(pMySql));
            return false;
        }
    }
#ifdef MYDEBUG
	int64_t rownum = mysql_affected_rows(pMySql);
	printf("file=%s,line=%d,affected rows=%lld,sqlerror=%s\n",
		__FILE__,__LINE__,rownum,mysql_error(pMySql));
#endif
    
    put(pMySql);

    return bRet;
}

bool CMysqlPool::UpdateRecord(const char *szSql)
{
    bool bRet = false;
    MYSQL *pMySql = get();
    if (pMySql == NULL && 0 != PS_mysql_maybe_reconnect(&pMySql))
    {
        put(pMySql);
	    mylogger("warning:mysqlerror:%s\n",mysql_error(pMySql));
        return bRet;
    }
    for (int i=0; i<2; ++i)
    {
        if (0 == mysql_query(pMySql, szSql))
        {
            bRet = true;
            break;
        }
        else if (0 == PS_mysql_maybe_reconnect(&pMySql))
        {
            continue;
        }
        else
        {
            put(pMySql);
	    mylogger("warning:mysqlerror:%s\n",mysql_error(pMySql));
            return false;
        }
    }

    //printf("total affected rows: %lld", mysql_affected_rows(pMySql));
    put(pMySql);
    
    return bRet;
}

bool CMysqlPool::DelRecord(const char *szSql)
{
    bool bRet = false;
    MYSQL *pMySql = get();
    if (pMySql == NULL && 0 != PS_mysql_maybe_reconnect(&pMySql))
    {
        put(pMySql);
	    mylogger("warning:mysqlerror:%s\n",mysql_error(pMySql));
        return bRet;
    }
    
    for (int i=0; i<2; ++i)
    {
        if (0 == mysql_query(pMySql, szSql))
        {
            bRet = true;
            break;
        }
        else if (0 == PS_mysql_maybe_reconnect(&pMySql))
        {
            continue;
        }
        else
        {
            put(pMySql);
	    mylogger("warning: mysqlerror:%s",mysql_error(pMySql));
            return false;
        }
    }
    
    put(pMySql);

    return bRet;
}

bool CMysqlPool::SelectDB(const char *szDB)
{
    bool bRet = false;
    MYSQL *pMySql = get();
    if (pMySql == NULL && 0 != PS_mysql_maybe_reconnect(&pMySql))
    {
        put(pMySql);
        return bRet;
    }
    
    if (mysql_select_db(pMySql, szDB))
        bRet = false;
    else
        bRet = true;
    
    put(pMySql);

    return bRet;
}

unsigned long long CMysqlPool::GetRowNum(MYSQL_RES *myquery)
{
    return mysql_num_rows(myquery);
}

MYSQL_ROW CMysqlPool::GetRecord(MYSQL_RES *myquery)
{
    return mysql_fetch_row(myquery);
}

unsigned int CMysqlPool::GetFieldNum(MYSQL_RES *myquery)
{
    return mysql_num_fields(myquery);
}

void CMysqlPool::FreeRecord(MYSQL_RES *myquery)
{
    mysql_free_result(myquery);
}

void CMysqlPool::SeekData(MYSQL_RES *myquery, int offset)
{
    mysql_data_seek(myquery,offset);
}

char * CMysqlPool::OutErrors(MYSQL *pMySql)
{
    return const_cast<char *>(mysql_error(pMySql));
}

char* CMysqlPool::GetFieldName(MYSQL_RES *myquery, int FieldNum)
{
    return mysql_fetch_field_direct(myquery, FieldNum)->name;
}

char * CMysqlPool::GetClientInfo()
{
    return const_cast<char *>(mysql_get_client_info());
}

//LOCK TABLES tbl1 READ, tbl2 WRITE
bool CMysqlPool::LockTable(const char *TableName, const char *Priority)
{
    bool bRet = false;
    char szSql[50];
    sprintf(szSql, "LOCK TABLES %s %s", TableName, Priority);
    MYSQL *pMySql = get();
    if (pMySql == NULL && 0 != PS_mysql_maybe_reconnect(&pMySql))
    {
        put(pMySql);
        return bRet;
    }

    for (int i=0; i<2; ++i)
    {
        if (0 == mysql_query(pMySql, szSql))
        {
            bRet = true;
            break;
        }
        else if (0 == PS_mysql_maybe_reconnect(&pMySql))
        {
            continue;
        }
        else
        {
            put(pMySql);
            return false;
        }
    }
    
    put(pMySql);

    return bRet;
}

bool CMysqlPool::UnlockTable()
{
    bool bRet = false;
    MYSQL *pMySql = get();
    if (pMySql == NULL && 0 != PS_mysql_maybe_reconnect(&pMySql))
    {
        put(pMySql);
        return bRet;
    }

    for (int i=0; i<2; ++i)
    {
        if (0 == mysql_query(pMySql, "UNLOCK TABLES"))
        {
            bRet = true;
            break;
        }
        else if (0 == PS_mysql_maybe_reconnect(&pMySql))
        {
            continue;
        }
        else
        {
            put(pMySql);
            return false;
        }
    }
    
    put(pMySql);

    return bRet;
}

